package dataset

import (
	"errors"
	"fmt"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/render"
	"gsc-datasets/internal/db/dao"
	pgdb "gsc-datasets/internal/db/sqlc"
	"gsc-datasets/internal/rest/constants"
	appMiddleware "gsc-datasets/internal/rest/middlewares"
	appRender "gsc-datasets/internal/rest/render"
	"gsc-datasets/internal/rest/routers"
	"gsc-datasets/pkg/util"
	"net/http"
)

type DatasetRouter struct {
	*routers.RestRouter
}

// New initialization
func New(
	rr *routers.RestRouter,
) *DatasetRouter {
	s := &DatasetRouter{
		rr,
	}
	s.register()
	return s
}

func (s *DatasetRouter) register() {
	r := s.ChiRouter()
	r.Use(appMiddleware.WithUser(s.RestRouter, constants.UserKey))
	r.With(appMiddleware.WithPager(s.RestRouter)).Get("/", s.listDatasets)
	r.Post("/", s.createDataset)
	r.Route(fmt.Sprintf("/{%s}", constants.DatasetKey), func(r chi.Router) {
		r.Use(appMiddleware.WithDataset(s.RestRouter, constants.DatasetKey))
		r.Get("/", s.getDataset)
		r.Put("/", s.updateDataset)
		r.Put("/unactive", s.unactiveDataset)
		r.Put("/active", s.activeDataset)
	})
}

// 列出dataset
func (s *DatasetRouter) listDatasets(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("AppRouter")
	ctx := r.Context()
	showCategory := r.URL.Query().Get("show_category")
	pagerPtr := ctx.Value(util.PagerKey{}).(*util.Pager)
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	apps, err := s.Dao().ListDatasetsAsCollaborator(ctx, pgdb.ListDatasetsAsCollaboratorParams{
		Status:       1,
		ShowCategory: showCategory,
		UserID:       userPtr.ID,
		Offset:       pagerPtr.Offset,
		Limit:        pagerPtr.Limit,
	})
	if err != nil {
		code := db.ExtractPgCode(&err)
		logger.Infof("pg err code=>%s", code)
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityListToRender(apps)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// / 新建app
type createDatasetRequest struct {
	*pgdb.UpsertDatasetParams
}

func (s *createDatasetRequest) Bind(r *http.Request) error {
	if s.UpsertDatasetParams == nil {
		return errors.New("缺少CreateDataset字段")
	}
	return nil
}

// 创建dataset
func (s *DatasetRouter) createDataset(w http.ResponseWriter, r *http.Request) {
	data := &createDatasetRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	data.UserID = userPtr.ID

	var datasetPtr *pgdb.Dataset
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		data.Code = fmt.Sprintf("qtd%s", util.RandomString(29)) // char(32)
		dataset, err := q.UpsertDataset(ctx, *data.UpsertDatasetParams)
		if err != nil {
			return
		}
		err = q.UpsertDatasetCollaborate(ctx, pgdb.UpsertDatasetCollaborateParams{
			DatasetID: dataset.ID,
			UserID:    dataset.UserID,
			Desc:      db.JNSFromString("owner"),
		})
		datasetPtr = &dataset
		return
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRender(datasetPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 获取dataset
func (s *DatasetRouter) getDataset(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	if err := render.Render(w, r, appRender.EntityToRender(datasetPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 更新dataset
type updateDatasetRequest struct {
	*pgdb.UpdateDatasetParams
}

func (s *updateDatasetRequest) Bind(r *http.Request) error {
	if s.UpdateDatasetParams == nil {
		return errors.New("缺少UpdateApp字段")
	}
	return nil
}

func (s *DatasetRouter) updateDataset(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetRouter")
	data := &updateDatasetRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	data.UpdateDatasetParams.ID = datasetPtr.ID
	if data.Vis == "" {
		data.Vis = datasetPtr.Vis
	}
	if data.Name == "" {
		data.Name = datasetPtr.Name
	}
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		dataset, err := q.UpdateDataset(ctx, *data.UpdateDatasetParams)
		code := db.ExtractPgCode(&err)
		logger.Infof("pgcode:%s", code)
		if err != nil {
			return
		}
		datasetPtr = &dataset
		return
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRender(datasetPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 停用dataset
func (s *DatasetRouter) unactiveDataset(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	err := s.Dao().UpdateDatasetStatus(ctx, pgdb.UpdateDatasetStatusParams{
		ID:     datasetPtr.ID,
		Status: 0,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.DefaultToRender()); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 启用dataset
func (s *DatasetRouter) activeDataset(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	err := s.Dao().UpdateDatasetStatus(ctx, pgdb.UpdateDatasetStatusParams{
		ID:     datasetPtr.ID,
		Status: 1,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.DefaultToRender()); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}
